/* * Copyright 2016 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.diffplug.spotless; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Logger; import java.util.stream.Collectors; import org.junit.Assert; import org.junit.ComparisonFailure; import org.junit.Rule; import org.junit.rules.TemporaryFolder; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import com.diffplug.common.io.Resources; public class ResourceHarness { /** * On OS X, the temp folder is a symlink, * and some of gradle's stuff breaks symlinks. * By only accessing it through the {@link #rootFolder()} * and {@link #newFile()} apis, we can guarantee there * will be no symlink problems. */ @Rule public TemporaryFolder folderDontUseDirectly = new TemporaryFolder(); /** Log nontruncated diff in case of a comparison failure to ease test development.*/ @Rule public TestWatcher logComparisonFailureDiff = new TestWatcher() { private static final String COMPARISON_SEPARATOR = "------------------------------------"; @Override protected void failed(Throwable e, Description description) { if (e instanceof ComparisonFailure) { ComparisonFailure failure = (ComparisonFailure) e; String msg = ""; msg += String.format("Output: %n%1$s%n%2$s%n%1$s%n", COMPARISON_SEPARATOR, failure.getActual()); msg += String.format("Expected:%n%1$s%n%2$s%n%1$s%n", COMPARISON_SEPARATOR, failure.getExpected()); logFailure(msg, description); } } private void logFailure(String message, Description description) { Logger log = Logger.getLogger(description.getClassName()); log.warning(String.format("Step '%s' failed.%n%s", description.getDisplayName(), message)); } }; /** Returns the root folder (canonicalized to fix OS X issue) */ protected File rootFolder() throws IOException { return folderDontUseDirectly.getRoot().getCanonicalFile(); } /** Returns a new child of the root folder. */ protected File newFile(String subpath) throws IOException { return new File(rootFolder(), subpath); } /** Returns a random child of the root folder. */ protected File newFile() throws IOException { return folderDontUseDirectly.newFile().getCanonicalFile(); } /** Writes the given content to the given path. */ protected File write(String path, String... lines) throws IOException { return write(path, LineEnding.UNIX, lines); } protected File write(String path, LineEnding ending, String... lines) throws IOException { return write(path, ending, StandardCharsets.UTF_8, lines); } protected File write(String path, LineEnding ending, Charset encoding, String... lines) throws IOException { String content = Arrays.stream(lines).collect(Collectors.joining(ending.str())) + ending.str(); Path target = newFile(path).toPath(); Files.createDirectories(target.getParent()); Files.write(target, content.getBytes(encoding)); return target.toFile(); } protected String read(Path path) throws IOException { return read(path, LineEnding.UNIX); } protected String read(String path) throws IOException { return read(path, LineEnding.UNIX); } protected String read(String path, LineEnding ending) throws IOException { return read(path, ending, StandardCharsets.UTF_8); } protected String read(Path path, LineEnding ending) throws IOException { return read(path, ending, StandardCharsets.UTF_8); } protected String read(String path, LineEnding ending, Charset encoding) throws IOException { Path target = newFile(path).toPath(); return read(target, ending, encoding); } protected String read(Path path, LineEnding ending, Charset encoding) throws IOException { String content = new String(Files.readAllBytes(path), encoding); String allUnixNewline = LineEnding.toUnix(content); return allUnixNewline.replace("\n", ending.str()); } protected void replace(String path, String toReplace, String replaceWith) throws IOException { String before = read(path); String after = before.replace(toReplace, replaceWith); if (before.equals(after)) { throw new IllegalArgumentException("Replace was ineffective! '" + toReplace + "' was not found in " + path); } write(path, after); } /** Returns the contents of the given file from the src/test/resources directory. */ protected static String getTestResource(String filename) throws IOException { URL url = ResourceHarness.class.getResource("/" + filename); if (url == null) { throw new IllegalArgumentException("No such resource " + filename); } return Resources.toString(url, StandardCharsets.UTF_8); } /** Returns Files (in a temporary folder) which has the contents of the given file from the src/test/resources directory. */ protected List<File> createTestFiles(String... filenames) throws IOException { List<File> files = new ArrayList<>(filenames.length); for (String filename : filenames) { files.add(createTestFile(filename)); } return files; } /** Returns a File (in a temporary folder) which has the contents of the given file from the src/test/resources directory. */ protected File createTestFile(String filename) throws IOException { int lastSlash = filename.lastIndexOf('/'); String name = lastSlash >= 0 ? filename.substring(lastSlash) : filename; File file = newFile(name); file.getParentFile().mkdirs(); Files.write(file.toPath(), getTestResource(filename).getBytes(StandardCharsets.UTF_8)); return file; } /** Returns a File (in a temporary folder) which has the given contents. */ protected File createTestFile(String filename, String content) throws IOException { File file = newFile(filename); file.getParentFile().mkdirs(); Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8)); return file; } /** Asserts that the given resource from the src/test/resources directory has the same content as the given file. */ protected void assertFileContent(String expectedContent, File actual) throws IOException { // This line thing is necessary for the tests to pass when Windows git screws up the line-endings String actualContent = new String(Files.readAllBytes(actual.toPath()), StandardCharsets.UTF_8); Assert.assertEquals(expectedContent, actualContent); } /** Reads the given resource from "before", applies the step, and makes sure the result is "after". */ protected void assertOnResources(FormatterStep step, String unformattedPath, String expectedPath) throws Throwable { assertOnResources(rawUnix -> step.format(rawUnix, new File("")), unformattedPath, expectedPath); } /** Reads the given resource from "before", applies the step, and makes sure the result is "after". */ protected void assertOnResources(FormatterFunc step, String unformattedPath, String expectedPath) throws Throwable { String unformatted = LineEnding.toUnix(getTestResource(unformattedPath)); // unix-ified input String formatted = step.apply(unformatted); // no windows newlines Assert.assertEquals(-1, formatted.indexOf('\r')); // unix-ify the test resource output in case git screwed it up String expected = LineEnding.toUnix(getTestResource(expectedPath)); // unix-ified output Assert.assertEquals(expected, formatted); } }